/**
 * Copyright (c) 2005-2012 https://github.com/zhangkaitao
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 */
package com.tyf.common.domain.search.utils;

import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.List;

import com.tyf.common.domain.search.Searchable;
import com.tyf.common.domain.search.exception.InvalidSearchValueException;
import com.tyf.common.domain.search.filter.SearchFilter;
import com.tyf.common.domain.search.exception.SearchException;
import com.tyf.common.domain.search.filter.OrCondition;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.InvalidPropertyException;
import org.springframework.core.convert.ConversionService;
import org.springframework.util.CollectionUtils;

import com.tyf.common.domain.search.SearchOperator;
import com.tyf.common.domain.search.exception.InvalidSearchPropertyException;
import com.tyf.common.domain.search.filter.AndCondition;
import com.tyf.common.domain.search.filter.Condition;
import com.tyf.common.util.SpringUtils;
import com.google.common.collect.Lists;

/**
 * <p>
 * User: Zhang Kaitao
 * <p>
 * Date: 13-1-15 上午11:46
 * <p>
 * Version: 1.0
 */
public final class SearchableConvertUtils {

    private static volatile ConversionService conversionService;
    
    private static final  String f1 = "yyyy-MM-dd HH:mm:ss";
    private static final  String f2 = "yyyy-MM-dd";

    /**
     * 设置用于类型转换的conversionService 把如下代码放入spring配置文件即可 <bean class=
     * "org.springframework.beans.factory.config.MethodInvokingFactoryBean">
     * <property name="staticMethod" value=
     * "com.sishuok.es.common.entity.search.utils.SearchableConvertUtils.setConversionService"/>
     * <property name="arguments" ref="conversionService"/> </bean>
     *
     * @param conversionService
     */
    public static void setConversionService(ConversionService conversionService) {
        SearchableConvertUtils.conversionService = conversionService;
    }

    public static ConversionService getConversionService() {
        if (conversionService == null) {
            synchronized (SearchableConvertUtils.class) {
                if (conversionService == null) {
                    try {
                        conversionService = SpringUtils.getBean(ConversionService.class);
                    } catch (Exception e) {
                        throw new SearchException(
                                "conversionService is null, " + "search param convert must use conversionService. "
                                        + "please see [com.sishuok.es.common.entity.search.utils."
                                        + "SearchableConvertUtils#setConversionService]");
                    }
                }
            }
        }
        return conversionService;
    }

    /**
     * @param search
     *            查询条件
     * @param entityClass
     *            实体类型
     * @param <T>
     */
    public static <T> void convertSearchValueToEntityValue(final Searchable search, final Class<T> entityClass) {

        if (search.isConverted()) {
            return;
        }

        Collection<SearchFilter> searchFilters = search.getSearchFilters();
        BeanWrapperImpl beanWrapper = new BeanWrapperImpl(entityClass);
        beanWrapper.setAutoGrowNestedPaths(true);
        beanWrapper.setConversionService(getConversionService());

        for (SearchFilter searchFilter : searchFilters) {
            convertSearchValueToEntityValue(beanWrapper, searchFilter);

        }
    }

    private static void convertSearchValueToEntityValue(BeanWrapperImpl beanWrapper, SearchFilter searchFilter) {
        if (searchFilter instanceof Condition) {
            Condition condition = (Condition) searchFilter;
            convert(beanWrapper, condition);
            return;
        }

        if (searchFilter instanceof OrCondition) {
            for (SearchFilter orFilter : ((OrCondition) searchFilter).getOrFilters()) {
                convertSearchValueToEntityValue(beanWrapper, orFilter);
            }
            return;
        }

        if (searchFilter instanceof AndCondition) {
            for (SearchFilter andFilter : ((AndCondition) searchFilter).getAndFilters()) {
                convertSearchValueToEntityValue(beanWrapper, andFilter);
            }
            return;
        }

    }

    private static void convert(BeanWrapperImpl beanWrapper, Condition condition) {
        String searchProperty = condition.getSearchProperty();

        // 自定义的也不转换
        if (condition.getOperator() == SearchOperator.custom) {
            return;
        }

        // 一元运算符不需要计算
        if (condition.isUnaryFilter()) {
            return;
        }

        String entityProperty = condition.getEntityProperty();
        
        SearchOperator sp = condition.getOperator();

        Object value = condition.getValue();

        Object newValue = null;
        boolean isCollection = value instanceof Collection;
        boolean isArray = value != null && value.getClass().isArray();
        if (isCollection || isArray) {
            List<Object> list = Lists.newArrayList();
            if (isCollection) {
                list.addAll((Collection) value);
            } else {
                list = Lists.newArrayList(CollectionUtils.arrayToList(value));
            }
            int length = list.size();
            for (int i = 0; i < length; i++) {
                list.set(i, getConvertedValue(beanWrapper, searchProperty, entityProperty, list.get(i)));
            }
            newValue = list;
        } else {
        	//日期类型特别处理
        	//TODO 更好的解决方案应该是提供自定义的conversionService,以fastjson反序列化实现
        	Class<?> propertyClazz = beanWrapper.getPropertyType(entityProperty);
        	if(propertyClazz!=null && Date.class.isAssignableFrom(propertyClazz) && (value instanceof String)){
        		newValue = convertDateValue(searchProperty, sp, entityProperty, value, condition);
        	}else{
        		newValue = getConvertedValue(beanWrapper, searchProperty, entityProperty, value);
        	}
        }
        condition.setValue(newValue);
    }

    private static Object getConvertedValue(
            final BeanWrapperImpl beanWrapper,
            final String searchProperty,
            final String entityProperty,
            final Object value) {

        Object newValue = null;
        try {

            beanWrapper.setPropertyValue(entityProperty, value);
            newValue = beanWrapper.getPropertyValue(entityProperty);
        } catch (InvalidPropertyException e) {
            throw new InvalidSearchPropertyException(searchProperty, entityProperty, e);
        } catch (Exception e) {
            throw new InvalidSearchValueException(searchProperty, entityProperty, value, e);
        }

        return newValue;
    }
    /**
     * 转换日期格式的属性为Date类型
     * @param searchProperty
     * @param sp
     * @param entityProperty
     * @param value
     * @return
     */
    private static Date convertDateValue(final String searchProperty, final SearchOperator sp, 
    		final String entityProperty, final Object value, Condition condition){
    	SimpleDateFormat sdf = null;
    	String dateStr = (String)value;
    	if(dateStr.length() == f1.length()){
    		sdf = new SimpleDateFormat(f1);
    	}
    	boolean isF2 = dateStr.length() == f2.length();
    	if(isF2){
    		sdf = new SimpleDateFormat(f2);
    	}
    	Date d = null;
		try{
	    	d = sdf.parse(dateStr);
			
	    	if(sp == SearchOperator.lte){		//如果为操作符为<=,则当前日期需要+1,且操作符需要变成<
	    		d = DateUtils.addDays(d, 1);
	    		condition.setOperator(SearchOperator.lt);
	    	}
	    	if(sp==SearchOperator.eq && isF2){
	    		condition.setSearchProperty("Date("+entityProperty+")");	//如果是日期类型的等于查询,则查询的属性拼接为Date(createTime)
	    																//以解决无法根据日期等于的查询,只针对了mysql的语法
	    	}
	    } catch (Exception e) {
	        throw new InvalidSearchValueException(searchProperty, entityProperty, value, e);
	    }
    	return d;
    }


}
